package com.cms.bs.app.shiro.session;

import com.alibaba.druid.util.StringUtils;
import com.cms.bs.config.Config;
import com.cms.bs.config.Constants;
import com.cms.bs.util.BaseTypeUtil;
import com.cms.bs.util.CommonUtil;
import com.cms.bs.util.Log4jUtil;
import com.cms.bs.util.RedisUtil;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.CachingSessionDAO;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;

/**
 * Created by jiang on 2016-11-15.
 */
public class CachingShiroSessionDao extends CachingSessionDAO {

    /**
     * The Redis key prefix for the sessions
     */
    private String keyPrefix = "shiro_session:";

    /**
     * 重写CachingSessionDAO中readSession方法，如果Session中没有登陆信息就调用doReadSession方法从Redis中重读
     */
    @Override
    public Session readSession(Serializable sessionId) throws UnknownSessionException {
        System.out.println("readSession...");
        Session session = getCachedSession(sessionId);
        if (session == null || session.getAttribute(Constants.CURRENT_USER) == null) {
            session = this.doReadSession(sessionId);
            if (session == null) {
                throw new UnknownSessionException("There is no session with id [" + sessionId + "]");
            } else {
                // 缓存
                cache(session, session.getId());
            }
        }
        return session;
    }

    @Override
    protected Session doReadSession(Serializable sessionId) {
        System.out.println("doReadSession...");

        Session session = null;
        if (sessionId == null) {
            Log4jUtil.error(this.getClass().getName(), "session id is null");
            return null;
        }
        String sessionStr = RedisUtil.get(getRedisKey(sessionId));
        if (!StringUtils.isEmpty(sessionStr)) {
            session = (Session) CommonUtil.deserialize(sessionStr);

            RedisUtil.remove(getRedisKey(session.getId()));
            RedisUtil.put(getRedisKey(session.getId()), CommonUtil.serialize(session), BaseTypeUtil.safeInt(Config.getPropObj("redis.expire")));
        }
        return session;
    }

    /**
     * 如DefaultSessionManager在创建完session后会调用该方法；
     * 如保存到关系数据库/文件系统/NoSQL数据库；即可以实现会话的持久化；
     * 返回会话ID；主要此处返回的ID.equals(session.getId())；
     */
    @Override
    protected Serializable doCreate(Session session) {
        System.out.println("doCreate...");
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);

        RedisUtil.remove(getRedisKey(session.getId()));
        RedisUtil.put(getRedisKey(sessionId), CommonUtil.serialize(session), 3600);
        return sessionId;
    }

    /**
     * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     */
    @Override
    protected void doUpdate(Session session) {
        System.out.println("doUpdate...");
        if (session == null || session.getId() == null) {
            Log4jUtil.error(this.getClass().getName(), "session or session id is null");
            return;
        }

        //如果会话过期/停止 没必要再更新了
        if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
            Log4jUtil.error(this.getClass().getName(), "session is expired");
            return;
        }

        if (session instanceof ShiroSession) {
            // 如果没有主要字段(除lastAccessTime以外其他字段)发生改变
            ShiroSession ss = (ShiroSession) session;
            if (!ss.isChanged()) {
                return;
            }
            ss.setChanged(false);
        }

        RedisUtil.remove(getRedisKey(session.getId()));
        RedisUtil.put(getRedisKey(session.getId()), CommonUtil.serialize(session), 3600);
    }

    /**
     * 删除会话；当会话过期/会话停止（如用户退出时）会调用
     */
    @Override
    protected void doDelete(Session session) {
        System.out.println("doDelete...");
        if (session == null || session.getId() == null) {
            Log4jUtil.error(this.getClass().getName(), "session or session id is null");
            return;
        }
        RedisUtil.remove(getRedisKey(session.getId()));
    }

    /**
     * 删除cache中缓存的Session
     */
    public void uncache(Serializable sessionId) {
        System.out.println("uncache...");
        Session session = this.readSession(sessionId);
        super.uncache(session);
    }

    /**
     * 获得byte[]型的key
     *
     * @param sessionId
     * @return
     */
    private String getRedisKey(Serializable sessionId) {
        return this.keyPrefix + sessionId;
    }

    /**
     * Returns the Redis session keys
     * prefix.
     *
     * @return The prefix
     */
    public String getKeyPrefix() {
        return keyPrefix;
    }

    /**
     * Sets the Redis sessions key
     * prefix.
     *
     * @param keyPrefix The prefix
     */
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

}
